/*
 * ========THE SOLMIX PROJECT=====================================
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */
package org.solmix.fmk.config.impl;

import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.xml.stream.events.EntityReference;

import org.apache.aries.blueprint.ParserContext;
import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
import org.apache.aries.blueprint.mutable.MutableCollectionMetadata;
import org.apache.aries.blueprint.mutable.MutableIdRefMetadata;
import org.apache.aries.blueprint.mutable.MutableRefMetadata;
import org.apache.aries.blueprint.mutable.MutableServiceMetadata;
import org.apache.aries.blueprint.mutable.MutableValueMetadata;
import org.osgi.service.blueprint.container.ComponentDefinitionException;
import org.osgi.service.blueprint.reflect.BeanArgument;
import org.osgi.service.blueprint.reflect.BeanProperty;
import org.osgi.service.blueprint.reflect.ComponentMetadata;
import org.osgi.service.blueprint.reflect.IdRefMetadata;
import org.osgi.service.blueprint.reflect.Metadata;
import org.osgi.service.blueprint.reflect.RefMetadata;
import org.osgi.service.blueprint.reflect.ValueMetadata;
import org.solmix.fmk.config.ConfigRealm;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Comment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * @version $Id$
 */
public class NamespaceHandler implements org.apache.aries.blueprint.NamespaceHandler
{
   public static final String DEFAULT_NAME = "default";

   public static final String SOLMIX_SERVICE_MODULE = "org.solmix.service.cm";

   public static final String SOLMIX_NS_CONIFG_BUNDLE = "config-bundle";

   public static final String SOLMIX_NS_CONIFG_MODULE = "module";

   public static final String SOLMIX_NS_CONIFG_CM = "cm";

   public static final String SOLMIX_NS_NAME = "name";

   public static final String SOLMIX_NS_RANK = "rank";

   public static final String SOLMIX_NS_BUNDLECONTEXT = "bundleContext";

   public static final String SOLMIX_NS_MODULES = "modules";

   public static final String BLUEPRINT_BUNDLE_CONTEXT = "blueprintBundleContext";

   public static final String SOLMIX_NS_MODULE_CMID = "cmid";

   public static final String SOLMIX_NS_MODULE_FLAGS = "flags";
   public static final String SOLMIX_NS_MODULE_REFERENCE_PERSISTENT_ID = "reference-persistent-id";

   public static final String SOLMIX_NS_MODULE_CM_MODULE = "cm";

   /**
    * {@inheritDoc}
    * 
    * @see org.apache.aries.blueprint.NamespaceHandler#decorate(org.w3c.dom.Node,
    *      org.osgi.service.blueprint.reflect.ComponentMetadata, org.apache.aries.blueprint.ParserContext)
    */
   @Override
   public ComponentMetadata decorate(Node arg0, ComponentMetadata arg1, ParserContext arg2)
   {
      throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported");
   }

   /**
    * {@inheritDoc}
    * 
    * @see org.apache.aries.blueprint.NamespaceHandler#getManagedClasses()
    */
   @SuppressWarnings("unchecked")
   @Override
   public Set<Class> getManagedClasses()
   {
      // return null;
      return new HashSet<Class>(Arrays.asList(ConfigRealmImpl.class, Module.class));
   }

   /**
    * {@inheritDoc}
    * 
    * @see org.apache.aries.blueprint.NamespaceHandler#getSchemaLocation(java.lang.String)
    */
   @Override
   public URL getSchemaLocation(String namespace)
   {
      return getClass().getResource("solmix-config-1.0.xsd");
   }

   /**
    * {@inheritDoc}
    * 
    * @see org.apache.aries.blueprint.NamespaceHandler#parse(org.w3c.dom.Element,
    *      org.apache.aries.blueprint.ParserContext)
    */
   @Override
   public Metadata parse(Element element, ParserContext context)
   {
      if (nodeNameEquals(element, SOLMIX_NS_CONIFG_BUNDLE)) {
         MutableBeanMetadata bean = context.createMetadata(MutableBeanMetadata.class);
         bean.setRuntimeClass(ConfigRealmImpl.class);
         String name = element.getAttribute(SOLMIX_NS_NAME);
         if (name == null || name.equals(""))
            name = DEFAULT_NAME;
         bean.addProperty(SOLMIX_NS_NAME, createValue(context, name));
         bean.addProperty(SOLMIX_NS_BUNDLECONTEXT, createRef(context, BLUEPRINT_BUNDLE_CONTEXT));
         String rank = element.getAttribute(SOLMIX_NS_RANK);
         if (rank != null && rank.length() > 0) {
            bean.addProperty(SOLMIX_NS_RANK, createValue(context, rank));
         }
         // chilsNodes
         Metadata modules = parseModuleElement(element, context);
         bean.addProperty(SOLMIX_NS_MODULES, modules);
         MutableServiceMetadata service = context.createMetadata(MutableServiceMetadata.class);
         service.setId(name);
         service.setServiceComponent(bean);
         service.addInterface(ConfigRealm.class.getName());
         service.addServiceProperty(createValue(context, SOLMIX_SERVICE_MODULE), createValue(context, name));
         return service;
      } else {
         throw new IllegalStateException("Unexpected element " + element.getNodeName());
      }
   }

   private Metadata parseModuleElement(Element element, ParserContext context)
   {
      // get module list
      NodeList childElements = element.getElementsByTagNameNS(element.getNamespaceURI(), SOLMIX_NS_CONIFG_MODULE);
      if (childElements != null && childElements.getLength() > 0) {
         MutableCollectionMetadata moduls = context.createMetadata(MutableCollectionMetadata.class);
         // begin <module ../>
         for (int i = 0; i < childElements.getLength(); i++) {
            Node child = childElements.item(i);
            if (child instanceof Element) {
               Element childElement = (Element) child;
               MutableBeanMetadata md = context.createMetadata(MutableBeanMetadata.class);
               md.setRuntimeClass(Module.class);
               md.addProperty(SOLMIX_NS_NAME, createValue(context, childElement.getAttribute(SOLMIX_NS_NAME)));
               if (childElement.getAttribute(SOLMIX_NS_MODULE_FLAGS) != null) {
                  md.addProperty(SOLMIX_NS_MODULE_FLAGS, createValue(context, childElement
                     .getAttribute(SOLMIX_NS_MODULE_FLAGS)));
               }
               if(childElement.getAttribute(SOLMIX_NS_MODULE_REFERENCE_PERSISTENT_ID)!=null){
                   md.addProperty("persistentId", createValue(context, childElement
                       .getAttribute(SOLMIX_NS_MODULE_REFERENCE_PERSISTENT_ID)));
               }
               NodeList cmList = element.getElementsByTagNameNS(element.getNamespaceURI(), SOLMIX_NS_CONIFG_CM);
               // begin <cm...></cm>
               if (cmList.item(0) instanceof Element) {
                  Element cm = (Element) cmList.item(0);
                  MutableBeanMetadata cmbean = parseCMElement(cm, md, context);
                  md.addProperty(SOLMIX_NS_MODULE_CM_MODULE, cmbean);
                  md.addProperty(SOLMIX_NS_MODULE_CMID, createIdRef(context, cmbean.getId()));
               }// end <cm../>
               moduls.addValue(md);
            }
         }// end <module ../>
         return moduls;
      }
      return null;
   }

   /**
    * @param cm
    * @param context
    */
   private MutableBeanMetadata parseCMElement(Element cm, ComponentMetadata md, ParserContext context)
   {
      MutableBeanMetadata cmbean = context.createMetadata(MutableBeanMetadata.class);
      cmbean.setClassName(cm.getAttribute("class"));
      cmbean.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
      cmbean.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
      cmbean.setId(context.generateId());
      // <property /> or <argument />
      NodeList children = cm.getChildNodes();
      for (int j = 0; j < children.getLength(); j++) {
         Node properties = children.item(j);
         if (properties instanceof Element) {
            Element pro = (Element) properties;
            if (nodeNameEquals(pro, "argument")) {
               cmbean.addArgument(context.parseElement(BeanArgument.class, md, pro));
            } else if (nodeNameEquals(pro, "property")) {
               cmbean.addProperty(context.parseElement(BeanProperty.class, md, pro));
            }
         }
      }
      String options = getTextValue(cm);
      if (options != null && options.length() > 0) {
         cmbean.addProperty("options", createValue(context, options));
      }
      // register modules
      context.getComponentDefinitionRegistry().registerComponentDefinition(cmbean);
      return cmbean;
   }

   private Metadata parseConfig(Element element, ParserContext context)
   {
      return null;

   }

   private static boolean nodeNameEquals(Node node, String name)
   {
      return (name.equals(node.getNodeName()) || name.equals(node.getLocalName()));
   }

   // private Metadata parseConfig(Element element, ParserContext context)
   // {
   // MutableBeanMetadata bean = context.createMetadata(MutableBeanMetadata.class);
   // bean.setRuntimeClass(ConfigRealmImpl.class);
   // String name = element.getAttribute("name");
   // if(name==null || name.equals(""))
   // name=DEFAULT_INSTANCE_NAME;
   // //blueprintBundleContext BP容器context
   // bean.addProperty("bundleContext", createRef(context, "blueprintBundleContext"));
   // bean.addProperty("name", createValue(context, name));
   // String rank = element.getAttribute("rank");
   // if (rank != null && rank.length() > 0) {
   // bean.addProperty("rank", createValue(context, rank));
   // }
   // NodeList childElements = element.getElementsByTagNameNS(element.getNamespaceURI(), "module");
   // if (childElements != null && childElements.getLength() > 0) {
   // MutableCollectionMetadata children = context.createMetadata(MutableCollectionMetadata.class);
   // for (int i = 0; i < childElements.getLength(); ++i) {
   // Element childElement = (Element) childElements.item(i);
   // MutableBeanMetadata md = context.createMetadata(MutableBeanMetadata.class);
   // md.setClassName(childElement.getAttribute("class"));
   // if (childElement.getAttribute("name") != null) {
   // md.addProperty("name", createValue(context, childElement.getAttribute("name")));
   // }
   // if (childElement.getAttribute("flags") != null) {
   // md.addProperty("flags", createValue(context, childElement.getAttribute("flags")));
   // }
   // String options = getTextValue(childElement);
   // if (options != null && options.length() > 0) {
   // md.addProperty("options", createValue(context, options));
   // }
   // children.addValue(md);
   // }
   // bean.addProperty("modules", children);
   // }
   // // Publish Config
   // MutableServiceMetadata service = context.createMetadata(MutableServiceMetadata.class);
   // service.setId(name);
   // service.setServiceComponent(bean);
   // service.addInterface(ConfigRealm.class.getName());
   // service.addServiceProperty( createValue(context, SOLMIX_NS_MODULE), createValue(context, name));
   // return service;
   // }

   private ValueMetadata createValue(ParserContext context, String value)
   {
      MutableValueMetadata v = context.createMetadata(MutableValueMetadata.class);
      v.setStringValue(value);
      return v;
   }

   private RefMetadata createRef(ParserContext context, String value)
   {
      MutableRefMetadata r = context.createMetadata(MutableRefMetadata.class);
      r.setComponentId(value);
      return r;
   }

   private IdRefMetadata createIdRef(ParserContext context, String id)
   {
      MutableIdRefMetadata idref = context.createMetadata(MutableIdRefMetadata.class);
      idref.setComponentId(id);
      return idref;
   }

   private static String getTextValue(Element element)
   {
      StringBuffer value = new StringBuffer();
      NodeList nl = element.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node item = nl.item(i);
         if ((item instanceof CharacterData && !(item instanceof Comment)) || item instanceof EntityReference) {
            value.append(item.getNodeValue());
         }
      }
      return value.toString();
   }
}
